﻿using System;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Collections.Generic;
using Kotonoha.Objects;
using Kotonoha.Objects.SuperMemo;
using Kotonoha.Server.Utils;

namespace Kotonoha.Server.Objects.Model {
    /// <summary>
    /// Represents a japanese word with reading and translations
    /// </summary>
    public class Word : INotifyPropertyChanged, IIdentifable
    {
        public Word() {
            id = -1;
            translations = new List<string>();
            examples = new List<Example>();
        }

        private long id;

        [XmlAttribute]
        public virtual long Id {
            get { return id; }
            set { id = value; }
        }

        private string writing;

        [XmlAttribute]
        public virtual string Writing {
            get { return writing; }
            set { 
                writing = value;
                FirePropertyChanged("Writing");
            }
        }

        private string reading;

        [XmlAttribute]
        public virtual string Reading {
            get { return reading; }
            set {
                reading = value;
                FirePropertyChanged("Reading");
            }
        }

        private IList<string> translations;
        public virtual IList<string> Translations {
            get { return translations; }
            set { translations = value; }
        }

        private WeakReference proxyList;
        public ObservableProxyList<string, ValueProxy<string>> TranslationProxies {
            get {
                ObservableProxyList<string, ValueProxy<string>> obj;
                if (proxyList == null || !proxyList.IsAlive) {
                    obj = new ObservableProxyList<string, ValueProxy<string>>(translations, ValueProxy<string>.Create, ValueProxy<string>.GetValue);
                    proxyList = new WeakReference(obj);
                } else {
                    obj = proxyList.Target as ObservableProxyList<string, ValueProxy<string>>;
                }
                return obj;
            }
        }

        private IList<Example> examples;
        public virtual IList<Example> Examples {
            get { return examples; }
            set { examples = value; }
        }


        private DateTime addedDate;

        [XmlAttribute]
        public virtual DateTime AddedDate {
            get { return addedDate; }
            set {
                addedDate = value;
                FirePropertyChanged("AddedDate");
            }
        }

        private ItemLearningData learningData;
        public virtual ItemLearningData LearningData {
            get { return learningData; }
            set {
                learningData = value;
                FirePropertyChanged("LearningData");
            }
        }

        private IList<Tag> tags = new List<Tag>();

        [XmlIgnore]
        public virtual IList<Tag> Tags {
            get { return tags; }
            set { tags = value; }
        }

        private IDictionary<string, string> additional = new Dictionary<string, string>();
        [XmlElement(ElementName = "Additional")]
        [XmlIgnore]
        public virtual IDictionary<string, string> Additional {
            get { return additional; }
            set { additional = value; }
        }

        public virtual int getAdditionalInt(string name, int defVal) {
            string s;
            if (!additional.TryGetValue(name, out s)) {
                return defVal;
            }
            return int.Parse(s);
        }

        public virtual bool Equals(Word obj) {
            if (ReferenceEquals(null, obj)) {
                return false;
            }
            if (ReferenceEquals(this, obj)) {
                return true;
            }
            return obj.Id == Id;
        }

        public override bool Equals(object obj) {
            if (ReferenceEquals(null, obj)) {
                return false;
            }
            if (ReferenceEquals(this, obj)) {
                return true;
            }
            if (obj.GetType() != typeof (Word)) {
                return false;
            }
            return Equals((Word) obj);
        }

        public override int GetHashCode() {
            return Id.GetHashCode();
        }

        public virtual event PropertyChangedEventHandler PropertyChanged;

        private void FirePropertyChanged(string name) {
            if (PropertyChanged != null) {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
            }
        }
    }

    public class ValueProxy<T> : INotifyPropertyChanged {
        public event PropertyChangedEventHandler PropertyChanged;

        public void InvokePropertyChanged(PropertyChangedEventArgs e) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, e);
        }

        private T value;
        public T Value {
            get { return value; }
            set { 
                this.value = value;
                InvokePropertyChanged(new PropertyChangedEventArgs("Value"));
            }
        }

        public static ValueProxy<T> Create(T value) {
            return new ValueProxy<T> {Value = value}; 
        }

        public static T GetValue(ValueProxy<T> proxy) {
            return proxy.Value;
        }
    }

    /// <summary>
    /// Represents example sentence
    /// </summary>
    public class Example : INotifyPropertyChanged {
        private string content;
        public string Content {
            get { return content; }
            set {
                content = value;
                InvokePropertyChanged("Content");
            }
        }

        private string translation;
        public string Translation {
            get { return translation; }
            set { 
                translation = value;
                InvokePropertyChanged("Translation");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void InvokePropertyChanged(string name) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(name));
        }
    }
}